# -*- coding: utf-8 -*-
# @Time    : 2024/10/29 15:47
# @Author  : CHNJX
# @File    : scheduler.py
# @Desc    : 时间调度器

import uuid
import time
from flask import current_app
import threading
from app.core.pipeline_monitor import PipelineMonitor
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.executors.pool import ThreadPoolExecutor

# 监控任务的唯一标识符
MONITORING_JOB_ID = 'strategy_monitoring_job'

class SchedulerService:
    """调度器服务类"""
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, app=None):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance.initialized = False
        return cls._instance

    def __init__(self, app=None):
        if not hasattr(self, 'initialized') or not self.initialized:
            self.app = app
            self.pipeline_monitor = PipelineMonitor(app)
            self.scheduled_jobs = {}
            
            # 初始化APScheduler
            self._init_scheduler()
            
            self.initialized = True
            if app is not None:
                self.init_app(app)

    def _init_scheduler(self):
        """初始化APScheduler"""
        try:
            # 配置执行器
            executors = {
                'default': ThreadPoolExecutor(20),
            }
            
            job_defaults = {
                'coalesce': False,
                'max_instances': 1
            }
            
            self.scheduler = BackgroundScheduler(
                executors=executors,
                job_defaults=job_defaults,
                timezone='Asia/Shanghai'
            )
            
            # 启动调度器
            if not self.scheduler.running:
                self.scheduler.start()
                
        except Exception as e:
            print(f"初始化调度器失败: {str(e)}")
            self.scheduler = None

    def init_app(self, app):
        """初始化应用"""
        self.app = app
        self.pipeline_monitor.init_app(app)
        
        # 确保调度器已启动
        if self.scheduler and not self.scheduler.running:
            try:
                self.scheduler.start()
                app.logger.info("APScheduler 已启动")
            except Exception as e:
                app.logger.error(f"启动调度器失败: {str(e)}")

    def start_monitoring_job(self, func, minutes=5):
        """启动或恢复策略监控的定时任务"""
        if not self.scheduler:
            raise RuntimeError("调度器未初始化")
            
        try:
            # 创建一个包装函数来确保任务在应用上下文中执行
            def wrapped_func():
                if self.app:
                    with self.app.app_context():
                        func()
                else:
                    func()
            
            # 使用 replace_existing=True 可以避免重复添加任务
            self.scheduler.add_job(
                id=MONITORING_JOB_ID, 
                func=wrapped_func, 
                trigger='interval', 
                minutes=minutes,
                replace_existing=True
            )
            
            if self.app:
                self.app.logger.info(f"策略监控任务 '{MONITORING_JOB_ID}' 已启动/更新，每 {minutes} 分钟运行一次")
            else:
                print(f"策略监控任务 '{MONITORING_JOB_ID}' 已启动/更新，每 {minutes} 分钟运行一次")
                
        except Exception as e:
            error_msg = f"启动监控任务失败: {str(e)}"
            if self.app:
                self.app.logger.error(error_msg)
            else:
                print(error_msg)
            raise

    def stop_monitoring_job(self):
        """停止策略监控的定时任务"""
        if not self.scheduler:
            return
            
        try:
            self.scheduler.remove_job(MONITORING_JOB_ID)
            if self.app:
                self.app.logger.info(f"策略监控任务 '{MONITORING_JOB_ID}' 已停止")
            else:
                print(f"策略监控任务 '{MONITORING_JOB_ID}' 已停止")
        except Exception:
            # 任务可能已经不存在，这不是一个错误
            if self.app:
                self.app.logger.info(f"尝试停止一个不存在的监控任务 '{MONITORING_JOB_ID}'，操作跳过")

    def is_monitoring_job_running(self):
        """检查策略监控任务是否存在且未暂停"""
        if not self.scheduler:
            return False
            
        try:
            job = self.scheduler.get_job(MONITORING_JOB_ID)
            return job is not None
        except Exception:
            return False

    def get_monitoring_job_info(self):
        """获取监控任务的详细信息"""
        if not self.scheduler:
            return None
            
        try:
            job = self.scheduler.get_job(MONITORING_JOB_ID)
            if job:
                return {
                    'id': job.id,
                    'name': job.name,
                    'trigger': str(job.trigger),
                    'next_run_time': job.next_run_time.isoformat() if job.next_run_time else None
                }
            return None
        except Exception:
            return None

    def schedule_job(self, trigger_time: str, device_name: str, job_params: dict) -> str:
        """创建任务监控"""
        job_id = str(uuid.uuid4())
        if device_name in self.scheduled_jobs:
            self.cancel_device_jobs(device_name)

        self.scheduled_jobs[device_name] = {
            "job_id": job_id,
            "params": job_params,
            "trigger_time": trigger_time
        }
        return job_id

    def cancel_device_jobs(self, device_name: str) -> bool:
        """取消设备的监控"""
        if device_name not in self.scheduled_jobs:
            return False

        # 停止监控
        self.pipeline_monitor.stop_monitor(device_name)
        del self.scheduled_jobs[device_name]
        return True

    def cancel_all(self) -> bool:
        """取消所有设备的所有任务"""
        res = True
        for device_name in list(self.scheduled_jobs.keys()):
            res = res == self.cancel_device_jobs(device_name)
        return res

    def cleanup(self):
        """完全清理所有监控资源"""
        try:
            # 1. 停止中央监控任务
            self.stop_monitoring_job()
            
            # 2. 停止管道监控
            if hasattr(self, 'pipeline_monitor') and self.pipeline_monitor:
                self.pipeline_monitor.stop_all()
            
            # 3. 清理所有调度任务
            if hasattr(self, 'scheduled_jobs'):
                self.scheduled_jobs.clear()
            
            # 4. 移除调度器中的所有任务
            if self.scheduler and self.scheduler.running:
                # 获取所有任务并移除
                jobs = self.scheduler.get_jobs()
                for job in jobs:
                    try:
                        self.scheduler.remove_job(job.id)
                    except Exception:
                        pass  # 忽略单个任务移除失败
                
                # 5. 完全关闭调度器和线程池
                self.scheduler.shutdown(wait=True)  # 等待任务完成再关闭
                
            # 6. 清空实例变量
            self.scheduler = None
            
            # 7. 触发垃圾回收
            import gc
            gc.collect()
            
            try:
                if self.app:
                    self.app.logger.info("调度器服务资源已完全释放")
                else:
                    print("调度器服务资源已完全释放")
            except RuntimeError:
                print("调度器服务资源已完全释放")
                
        except Exception as e:
            # 避免在没有应用上下文的线程中使用current_app.logger
            try:
                if hasattr(self, 'app') and self.app:
                    self.app.logger.error(f"清理监控服务失败: {str(e)}")
                else:
                    print(f"清理监控服务失败: {str(e)}")
            except RuntimeError:
                # 如果没有应用上下文，使用标准的print输出
                print(f"清理监控服务失败: {str(e)}")

    def __del__(self):
        """析构函数，确保调度器被正确关闭"""
        try:
            if hasattr(self, 'scheduler') and self.scheduler and self.scheduler.running:
                self.scheduler.shutdown(wait=False)
        except Exception:
            pass

